﻿// <copyright file="SerialPortUtil.cs" company="DYJ">
// Copyright (c) DYJ. All rights reserved.
// </copyright>
// <author>zhiqiang li</author>
// <date> 2024-4-01 </date>
// <summary>串口开发辅助类</summary>
// 用法描述:
//     - 基类，业务类继承引用该基类；
//**********************************************************************************
// <remarks>
//     修改人：zhiqiang li
//     修改时间：2023-04-01
//     修改描述：建立文档
//     版本：1.1.0
//     修改日志：建立文档
//</remarks>
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace CommonUtils.Utils
{
    public delegate void SerialPortDataReceivedDelegate(byte[] ReceivedData);
    //public delegate void SerialPortDataReceivedDelegate(string ReceivedData);
    /// <summary>
    /// 串口开发辅助类
    /// </summary>
    public class SerialPortUtil
    {
        /// <summary>
        /// 接收事件是否有效 false表示有效
        /// </summary>
        public bool ReceiveEventFlag = false;
        /// <summary>
        /// 结束符比特
        /// </summary>
        public byte EndByte1 = 0x23;//string End = "#";
        public byte EndByte2 = 0x29;//string End = ")";

        public bool RecAndSendFlag = false;//用于标识收发状态，收发不能同时进行

        private readonly byte[] heartBeatOverviewLight = new byte[7] { 00, 00, 00, 00, 00, 00, 00 };

        /// <summary>
        /// 完整协议的记录处理事件
        /// </summary>
        public event SerialErrorReceivedEventHandler Error;

        public event SerialPortDataReceivedDelegate DataReceivedEvent;

        /// <summary>
        /// 连接状态变更时发生
        /// </summary>
        public Action<bool> OnConnectStatusChanged;

        #region 变量属性
        string _portName = "COM3";//串口号，默认COM1
        SerialPortBaudRates _baudRate = SerialPortBaudRates.BaudRate_115200;//波特率
        private Parity _parity = Parity.None;//校验位
        private StopBits _stopBits = StopBits.One;//停止位
        private SerialPortDatabits _dataBits = SerialPortDatabits.EightBits;//数据位

        private SerialPort comPort = new SerialPort();

        /// <summary>
        /// 串口号
        /// </summary>
        public string PortName
        {
            get { return _portName; }
            set { _portName = value; }
        }

        /// <summary>
        /// 波特率
        /// </summary>
        public SerialPortBaudRates BaudRate
        {
            get { return _baudRate; }
            set { _baudRate = value; }
        }

        /// <summary>
        /// 奇偶校验位
        /// </summary>
        public Parity Parity
        {
            get { return _parity; }
            set { _parity = value; }
        }

        /// <summary>
        /// 数据位
        /// </summary>
        public SerialPortDatabits DataBits
        {
            get { return _dataBits; }
            set { _dataBits = value; }
        }

        /// <summary>
        /// 停止位
        /// </summary>
        public StopBits StopBits
        {
            get { return _stopBits; }
            set { _stopBits = value; }
        }

        public void SetPortName(string PortName)
        {
            _portName = PortName;
        }

        public void SetBaudRate(SerialPortBaudRates BaudRate)
        {
            _baudRate = BaudRate;
        }

        #endregion

        #region 构造函数
        /// <summary>
        /// 参数构造函数（使用枚举参数构造）
        /// </summary>
        /// <param name="baud">波特率</param>
        /// <param name="par">奇偶校验位</param>
        /// <param name="sBits">停止位</param>
        /// <param name="dBits">数据位</param>
        /// <param name="name">串口号</param>
        public SerialPortUtil(string name, SerialPortBaudRates baud, Parity par, SerialPortDatabits dBits, StopBits sBits)
        {
            _portName = name;
            _baudRate = baud;
            _parity = par;
            _dataBits = dBits;
            _stopBits = sBits;

            comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
            comPort.ErrorReceived += new SerialErrorReceivedEventHandler(comPort_ErrorReceived);
        }

        /// <summary>
        /// 参数构造函数（使用字符串参数构造）
        /// </summary>
        /// <param name="baud">波特率</param>
        /// <param name="par">奇偶校验位</param>
        /// <param name="sBits">停止位</param>
        /// <param name="dBits">数据位</param>
        /// <param name="name">串口号</param>
        public SerialPortUtil(string name, string baud, string par, string dBits, string sBits)
        {
            _portName = name;
            _baudRate = (SerialPortBaudRates)Enum.Parse(typeof(SerialPortBaudRates), baud);
            _parity = (Parity)Enum.Parse(typeof(Parity), par);
            _dataBits = (SerialPortDatabits)Enum.Parse(typeof(SerialPortDatabits), dBits);
            _stopBits = (StopBits)Enum.Parse(typeof(StopBits), sBits);

            comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
            comPort.ErrorReceived += new SerialErrorReceivedEventHandler(comPort_ErrorReceived);
        }

        Thread _threadHeartBeat = null;
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public SerialPortUtil()
        {
            _portName = "COM3"; 
            _baudRate = SerialPortBaudRates.BaudRate_115200;
            _parity = Parity.None;
            _dataBits = SerialPortDatabits.EightBits;
            _stopBits = StopBits.One;

            //comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
            //comPort.ErrorReceived += new SerialErrorReceivedEventHandler(comPort_ErrorReceived);
            //comPort.PinChanged += ComPort_PinChanged;
        }

        private string deviceName = string.Empty;
        /// <summary>
        /// 启动心跳检测线程
        /// </summary>
        /// <param name="devName">设备名</param>
        public void StartHeartBeat(string devName)
        {
            deviceName = devName;
            _threadHeartBeat = new Thread(heartBeat);
            _threadHeartBeat.Name = $"串口心跳检测线程-{deviceName}";
            _threadHeartBeat.IsBackground = true;
            _threadHeartBeat.Start();
        }

        /// <summary>
        /// 心跳检测方法体
        /// </summary>
        private void heartBeat()
        {
            bool isOK = false;
            while (true) {
                try
                {
                    if (comPort != null)
                    {
                        switch (deviceName)
                        {
                            case "Nosepiece":
                                comPort.Write("heartbeat");
                                break;
                            case "OverviewLight":
                                comPort.Write(heartBeatOverviewLight, 0, heartBeatOverviewLight.Length);
                                break;
                        }
                        isOK = true;
                    }
                }
                catch (InvalidOperationException ex)
                {
                    // 当串口未打开，尝试进行通信时会捕获到InvalidOperationException
                    LogRun.Output("串口可能已断开: " + ex.Message);
                    isOK = false;
                }
                catch (IOException ex)
                {
                    // 当物理连接断开时，尝试进行通信可能会捕获到IOException
                    LogRun.Output("串口可能已断开: " + ex.Message);
                    isOK = false;
                }
                if (!isOK)
                {
                    //尝试重连
                    while (true)
                    {
                        if (comPort.IsOpen) comPort.Close();

                        try
                        {
                            comPort.Open();
                        }
                        catch (Exception ex)
                        {
                            isOK = false;
                            OnConnectStatusChanged?.Invoke(isOK);
                        }
                        Thread.Sleep(1000);
                        if(comPort.IsOpen)
                        {
                            isOK = true;
                            break;
                        }
                    }
                }
                OnConnectStatusChanged?.Invoke(isOK);
                Thread.Sleep(1000);
            }
        }

        #endregion

        /// <summary>
        /// 端口是否已经打开
        /// </summary>
        public bool IsOpen
        {
            get
            {
                return comPort.IsOpen;
            }
        }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <returns></returns>
        public bool OpenPort()
        {
            if (comPort.IsOpen) comPort.Close();

            comPort.PortName = _portName;
            comPort.BaudRate = (int)_baudRate;
            comPort.Parity = _parity;
            comPort.DataBits = (int)_dataBits;
            comPort.StopBits = _stopBits;
            comPort.ReadTimeout = 800;
            //comPort.WriteBufferSize = 70;//hlping 20220706
            //comPort.RtsEnable = true;

            comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
            comPort.ErrorReceived += new SerialErrorReceivedEventHandler(comPort_ErrorReceived);

            try
            {
                comPort.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 关闭端口
        /// </summary>
        public void ClosePort()
        {
            if (comPort.IsOpen)
            {
                comPort.DataReceived -= new SerialDataReceivedEventHandler(comPort_DataReceived);
                comPort.ErrorReceived -= new SerialErrorReceivedEventHandler(comPort_ErrorReceived);

                comPort.Close();
            }
        }

        /// <summary>
        /// 丢弃来自串行驱动程序的接收和发送缓冲区的数据
        /// </summary>
        public void DiscardBuffer()
        {
            comPort.DiscardInBuffer();
            comPort.DiscardOutBuffer();
        }

        #region 根据结束字节来判断是否全部获取完成
        List<byte> _byteData = new List<byte>();
        bool found = false;//是否检测到结束符号
        /// <summary>
        /// 数据接收处理
        /// </summary>
        void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //禁止接收事件时直接退出
            //if (ReceiveEventFlag) return;

            //System.Threading.Thread.Sleep(100);
            _byteData.Clear();
            found = false;
            try
            {
                while (comPort.BytesToRead > 0 && !found)
                {
                    byte[] readBuffer = new byte[comPort.ReadBufferSize + 1];
                    int count = comPort.Read(readBuffer, 0, comPort.ReadBufferSize);
                    for (int i = 0; i < count; i++)
                    {
                        _byteData.Add(readBuffer[i]);

                        //if (readBuffer[i] == EndByte1 || readBuffer[i] == EndByte2)
                        if (i == count-1)
                        {
                            found = true;
                            GetAllDataHandle();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //LogRun.Output(ex.ToString());
            }

            #endregion
        }

        private void GetAllDataHandle()
        {
            if (DataReceivedEvent != null)
            {
                comPort.DiscardOutBuffer();       //清空发送缓冲区
                Thread.Sleep(100);
                byte[] bytes = _byteData.ToArray();
                DataReceivedEvent(bytes);
                //LogRun.Output(UTF8ByteToStr(_byteData.ToArray()));                
            }
            _byteData.Clear();
            found = false;
        }

        /// <summary>
        /// 错误处理函数
        /// </summary>
        void comPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            Error?.Invoke(sender, e);
        }

        #region 数据写入操作

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg"></param>
        public void WriteData(string msg)
        {
            if (!(comPort.IsOpen)) comPort.Open();

            comPort.Write(msg);
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg">写入端口的字节数组</param>
        public void WriteData(byte[] msg)
        {
            if (!(comPort.IsOpen)) comPort.Open();

            comPort.Write(msg, 0, msg.Length);
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg">包含要写入端口的字节数组</param>
        /// <param name="offset">参数从0字节开始的字节偏移量</param>
        /// <param name="count">要写入的字节数</param>
        public void WriteData(byte[] msg, int offset, int count)
        {
            if (!(comPort.IsOpen)) comPort.Open();

            comPort.Write(msg, offset, count);
        }

        /// <summary>
        /// 发送串口命令
        /// </summary>
        /// <param name="SendData">发送数据</param>
        /// <param name="ReceiveData">接收数据</param>
        /// <param name="Overtime">重复次数</param>
        /// <returns></returns>
        public int SendCommand(byte[] SendData, ref byte[] ReceiveData, int Overtime)
        {
            if (!(comPort.IsOpen)) comPort.Open();

            ReceiveEventFlag = true;        //关闭接收事件
            comPort.DiscardInBuffer();      //清空接收缓冲区                 
            comPort.Write(SendData, 0, SendData.Length);

            int num = 0, ret = 0;
            while (num++ < Overtime)
            {
                if (comPort.BytesToRead >= ReceiveData.Length) break;
                System.Threading.Thread.Sleep(1);
            }

            if (comPort.BytesToRead >= ReceiveData.Length)
            {
                ret = comPort.Read(ReceiveData, 0, ReceiveData.Length);
            }

            ReceiveEventFlag = false;       //打开事件
            return ret;
        }

        /// <summary>
        /// 发送串口命令
        /// </summary>
        /// <param name="SendData">发送数据</param>
        /// <param name="ReceiveData">接收数据</param>
        /// <param name="Overtime">重复次数</param>
        /// <returns></returns>
        public int SendCommand(string SendData, ref byte[] ReceiveData, int Overtime)
        {
            if (!(comPort.IsOpen)) comPort.Open();

            System.Threading.Thread.Sleep(100);
            ReceiveEventFlag = true;        //关闭接收事件

            comPort.DiscardOutBuffer();       //清空发送缓冲区
            comPort.DiscardInBuffer();      //清空接收缓冲区

            string[] txts = SendData.Split(' ');
            string txtssend = "";
            for (int i = 0; i < txts.Length; i++)
            {
                txtssend += txts[i];
            }
            string txtsend = txtssend;
            byte[] bytes = HexStringToByte(SendData);


            //byte[] bytes = System.Text.Encoding.Default.GetBytes(SendData);
            int ret = 0;
            if (bytes.Length > 0)
            {
                comPort.Write(bytes, 0, bytes.Length);
                LogRun.Output(SendData);

                int num = 0;
                while (num++ < Overtime)
                {
                    //if (comPort.BytesToRead >= ReceiveData.Length) break;
                    System.Threading.Thread.Sleep(1);
                }

                //if (comPort.BytesToRead >= ReceiveData.Length)
                //{
                    ret = comPort.Read(ReceiveData, 0, ReceiveData.Length);
                //}

                ReceiveEventFlag = false;       //打开事件

            }

            return ret;
        }

        /// <summary>
        /// 发送串口命令
        /// </summary>
        /// <param name="SendData">发送数据</param>
        /// <param name="Overtime">重复次数</param>
        /// <returns></returns>
        public bool SendCommand(byte[] SendData)
        {
            if (!(comPort.IsOpen)) return false;

            ReceiveEventFlag = true;        //关闭接收事件
            comPort.DiscardInBuffer();      //清空接收缓冲区                 
            comPort.Write(SendData, 0, SendData.Length);

            ReceiveEventFlag = false;       //打开事件
            return true;
        }

        /// 发送串口命令
        /// </summary>
        /// <param name="SendData">发送数据</param>
        /// <returns></returns>
        public bool SendCommand(string SendData)
        {
            if (!(comPort.IsOpen)) return false;

            try
            {
                System.Threading.Thread.Sleep(100);
                //ReceiveEventFlag = true;        //关闭接收事件

                string[] txts = SendData.Split(' ');
                string txtssend = "";
                for (int i = 0; i < txts.Length; i++)
                {
                    txtssend += txts[i];
                }
                string txtsend = txtssend;


                comPort.DiscardInBuffer();        //清空接收缓冲区
                comPort.DiscardOutBuffer();       //清空发送缓冲区

                //byte[] bytes = System.Text.Encoding.Default.GetBytes(SendData);
                byte[] bytes = HexStringToByte(SendData);
                if (bytes.Length > 0)
                {
                    //ReceiveEventFlag = false;       //打开事件
                    comPort.Write(bytes, 0, bytes.Length);
                    LogRun.Output($"物镜转盘发送数据:{SendData}");
                }
            }
            catch (Exception)
            {

            }
            return true;
        }
        public byte[] HexStringToByte(string strHex)
        {
            strHex = strHex.Replace(" ", "").Replace(Environment.NewLine, "");  //去除多余空格            
            int nLen = strHex.Length;
            nLen = nLen / 2;
            byte[] btArrayCmd = new byte[nLen];

            for (int i = 0; i < nLen; i++)
            {
                btArrayCmd[i] = Convert.ToByte(strHex.Substring(i * 2, 2), 16);

            }
            return btArrayCmd;
        }
        #endregion

        #region 常用的列表数据获取和绑定操作

        /// <summary>
        /// 封装获取串口号列表
        /// </summary>
        /// <returns></returns>
        public string[] GetPortNames()
        {
            return SerialPort.GetPortNames();
        }

        /// <summary>
        /// 设置串口号
        /// </summary>
        /// <param name="obj"></param>
        public static void SetPortNameValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (string str in SerialPort.GetPortNames())
            {
                obj.Items.Add(str);
            }
        }

        /// <summary>
        /// 设置串口号
        /// </summary>
        /// <param name="obj"></param>
        public static void SetPortNameValues(List<string> obj)
        {
            obj.Clear();
            foreach (string str in SerialPort.GetPortNames())
            {
                obj.Add(str);
            }
        }

        /// <summary>
        /// 设置波特率
        /// </summary>
        public static void SetBauRateValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (SerialPortBaudRates rate in Enum.GetValues(typeof(SerialPortBaudRates)))
            {
                obj.Items.Add(((int)rate).ToString());
            }
        }

        /// <summary>
        /// 设置波特率
        /// </summary>
        public static void SetBauRateValues(List<string> obj)
        {
            obj.Clear();
            foreach (SerialPortBaudRates rate in Enum.GetValues(typeof(SerialPortBaudRates)))
            {
                obj.Add(((int)rate).ToString());
            }
        }

        /// <summary>
        /// 设置数据位
        /// </summary>
        public static void SetDataBitsValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (SerialPortDatabits databit in Enum.GetValues(typeof(SerialPortDatabits)))
            {
                obj.Items.Add(((int)databit).ToString());
            }
        }


        /// <summary>
        /// 设置数据位
        /// </summary>
        public static void SetDataBitsValues(List<string> obj)
        {
            obj.Clear();
            foreach (SerialPortDatabits databit in Enum.GetValues(typeof(SerialPortDatabits)))
            {
                obj.Add(((int)databit).ToString());
            }
        }

        /// <summary>
        /// 设置校验位列表
        /// </summary>
        public static void SetParityValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (string str in Enum.GetNames(typeof(Parity)))
            {
                obj.Items.Add(str);
            }
            //foreach (Parity party in Enum.GetValues(typeof(Parity)))
            //{
            //    obj.Items.Add(((int)party).ToString());
            //}
        }

        /// <summary>
        /// 设置校验位列表
        /// </summary>
        public static void SetParityValues(List<string> obj)
        {
            obj.Clear();
            foreach (string str in Enum.GetNames(typeof(Parity)))
            {
                obj.Add(str);
            }
            //foreach (Parity party in Enum.GetValues(typeof(Parity)))
            //{
            //    obj.Items.Add(((int)party).ToString());
            //}
        }

        /// <summary>
        /// 设置停止位
        /// </summary>
        public static void SetStopBitValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (string str in Enum.GetNames(typeof(StopBits)))
            {
                obj.Items.Add(str);
            }
        }

        /// <summary>
        /// 设置停止位
        /// </summary>
        public static void SetStopBitValues(List<string> obj)
        {
            obj.Clear();
            foreach (string str in Enum.GetNames(typeof(StopBits)))
            {
                obj.Add(str);
            }
        }

        #endregion

        #region 格式转换
        /// <summary>
        /// 转换十六进制字符串到字节数组
        /// </summary>
        /// <param name="msg">待转换字符串</param>
        /// <returns>字节数组</returns>
        public static byte[] HexToByte(string msg)
        {
            msg = msg.Replace(" ", "");//移除空格

            //create a byte array the length of the
            //divided by 2 (Hex is 2 characters in length)
            byte[] comBuffer = new byte[msg.Length / 2];
            for (int i = 0; i < msg.Length; i += 2)
            {
                //convert each set of 2 characters to a byte and add to the array
                comBuffer[i / 2] = (byte)Convert.ToByte(msg.Substring(i, 2), 16);
            }

            return comBuffer;
        }

        /// <summary>
        /// 转换字节数组到十六进制字符串
        /// </summary>
        /// <param name="comByte">待转换字节数组</param>
        /// <returns>十六进制字符串</returns>
        public string ByteToHex(byte[] comByte)
        {
            StringBuilder builder = new StringBuilder(comByte.Length * 3);
            foreach (byte data in comByte)
            {
                builder.Append(Convert.ToString(data, 16).PadLeft(2, '0').PadRight(3, ' '));
            }

            return builder.ToString().ToUpper();
        }

        /// <summary>
        /// byte数组转字符串，编码UTF8
        /// </summary>
        /// <param name="comByte"></param>
        /// <returns></returns>
        public string UTF8ByteToStr(byte[] comByte)
        {
            string str = System.Text.Encoding.UTF8.GetString(comByte);
            return str;

        }

        /// <summary>
        /// 字符串转byte数组，编码UTF8
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public byte[] StrToUTF8Byte(string str)
        {
            byte[] decBytes = System.Text.Encoding.UTF8.GetBytes(str);
            return decBytes;
        }
        #endregion

        /// <summary>
        /// 检查端口名称是否存在
        /// </summary>
        /// <param name="port_name"></param>
        /// <returns></returns>
        public static bool Exists(string port_name)
        {
            foreach (string port in SerialPort.GetPortNames()) if (port == port_name) return true;
            return false;
        }

        /// <summary>
        /// 格式化端口相关属性
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static string Format(SerialPort port)
        {
            return String.Format("{0} ({1},{2},{3},{4},{5})",
                port.PortName, port.BaudRate, port.DataBits, port.StopBits, port.Parity, port.Handshake);
        }
    }

    public class DataReceivedEventArgs : EventArgs
    {
        public string DataReceived;
        public DataReceivedEventArgs(string m_DataReceived)
        {
            this.DataReceived = m_DataReceived;
        }
    }

    public delegate void DataReceivedEventHandler(DataReceivedEventArgs e);

    /// <summary>
    /// 串口数据位列表（5,6,7,8）
    /// </summary>
    public enum SerialPortDatabits : int
    {
        FiveBits = 5,
        SixBits = 6,
        SeventBits = 7,
        EightBits = 8
    }

    /// <summary>
    /// 串口波特率列表。
    /// 75,110,150,300,600,1200,2400,4800,9600,14400,19200,28800,38400,56000,57600,
    /// 115200,128000,230400,256000
    /// </summary>
    public enum SerialPortBaudRates : int
    {
        //BaudRate_75 = 75,
        //BaudRate_110 = 110,
        //BaudRate_150 = 150,
        //BaudRate_300 = 300,
        //BaudRate_600 = 600,
        //BaudRate_1200 = 1200,
        //BaudRate_2400 = 2400,
        //BaudRate_4800 = 4800,
        BaudRate_9600 = 9600,
        //BaudRate_14400 = 14400,
        BaudRate_19200 = 19200,
        //BaudRate_28800 = 28800,
        BaudRate_38400 = 38400,
        //BaudRate_56000 = 56000,
        //BaudRate_57600 = 57600,
        BaudRate_115200 = 115200,
        //BaudRate_128000 = 128000,
        //BaudRate_230400 = 230400,
        //BaudRate_256000 = 256000
    }
}

